#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include "Socket.hpp"

using std::string;
static const string Splitting_symbol = "\r\n";
static const std::string wwwroot = "./wwwroot"; // web 根目录
static const std::string homepage = "index.html";
static const std::string file_seq = "/";
struct ThreadData
{
    int sockfd;
};

class HttpRequest
{
public:
    void Deserialize(std::string package)
    {
        while (true)
        {
            std::size_t pos = package.find(Splitting_symbol);
            if (pos == std::string::npos)
                break;
            std::string temp = package.substr(0, pos);
            reqHeader_.push_back(temp);
            package.erase(0, pos + Splitting_symbol.size());
        }
        // 这里没有对接受到正文做检查，默认认为他是合法报文！二次复习的时候做修改
        reqText_ = package;
    }

    // 对请求行做反序列化
    void parse()
    {
        std::istringstream iss(reqHeader_[0]);
        iss >> method >> url >> http_version;
    }

    // 根据请求行的url做跳转
    void changePath()
    {
        path_ = "./wwwroot";
        if (url == "/" || url == "/index.html")
        {
            path_ += file_seq;
            path_ += homepage;
        }
        else
        {
            path_ += url;
        }
    }

    void DebugPrint()
    {
        // std::cout << "Debug!" << std::endl;
        // for (auto &val : reqHeader_)
        // {
        //     std::cout << val << std::endl;
        // }

        std::cout << "my current url is " << url << std::endl;
        std::cout << "will jump is " <<path_ << std::endl;
    }

public:
    std::vector<string> reqHeader_;
    std::string reqText_;
    std::string path_; // url请求路径

    std::string method;
    std::string url;
    std::string http_version;
};

// 自己写的:有bug,二次复习的时候修改,这里的逻辑是将发过来的报头全部写到vector中
// class HttpRequest
// {
// public:
//     void Deserialize(std::string package)
//     {
//         size_t pos = package.find(Splitting_symbol);
//         while (pos != std::string::npos)
//         {
//             std::string line = package.substr(0, pos);
//             req_.push_back(line);
//             package.erase(pos);
//             pos = package.find(Splitting_symbol);
//         }
//     }
//     void DebugPrint()
//     {
//         for (auto val : req_)
//         {
//             std::cout << val << std::endl;
//         }
//     }
// public:
//     std::vector<string> req_;
// };

class HttpServer
{
public:
    HttpServer(uint16_t port)
        : port_(port) {}
    HttpServer() {}
    bool run()
    {
        listenSock_.Socket();
        listenSock_.Bind(port_);
        listenSock_.Listen();

        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = listenSock_.Accept(&clientip, &clientport);
            pthread_t tid;
            ThreadData *td = new ThreadData();
            td->sockfd = sockfd;
            pthread_create(&tid, nullptr, ThreadRun, (void *)td);
        }
    }

    // htmlpath 是web目录名
    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        std::ifstream in(htmlpath, std::ios::binary); // 打开web目录
        if (!in.is_open())
            return "404";

        // 因为显示网页要用二进制读写，所以只能用seekg和tellg了
        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);
        in.read((char *)content.c_str(), content.size());

        return content;
        // std::string content;
        // std::string line;
        // /*
        //     getline的使用：in是文件对象，默认是由/n作为分割符，然后将
        //     in的每一个写入到line这个string对象中。
        // */
        // while (std::getline(in, line))
        // {
        //     content += line;
        // }
        // in.close();
        // return content;
    }

    // 处理http请求
    static void Httphandler(int fd)
    {
        char buffer[10240];
        ssize_t recv_ret = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (recv_ret > 0)
        {
            buffer[recv_ret] = 0;
            // lg(INFO, "recv a request %s", buffer);
            std::cout << "recv a request\n"
                      << buffer << std::endl;
            HttpRequest httpreq;
            httpreq.Deserialize(buffer);
            httpreq.parse();
            httpreq.changePath();
            httpreq.DebugPrint();

            // // 响应
            string response_line = "HTTP/1.1 200 OK"; // 响应状态行
            response_line += Splitting_symbol;

            string response_header = "Content-Length: ";           // 报文报头
            string response_text = ReadHtmlContent(httpreq.path_); // 报文正文
            if(response_text.empty())
            {
                response_text = ReadHtmlContent("./wwwroot/err.html");
                response_line = "HTTP/1.1 404 Not Found";
            }
            
            int text_len = response_text.size();
            response_header += std::to_string(text_len);
            response_header += Splitting_symbol;
            response_header += "Content-Type: text/html";
            response_header += Splitting_symbol;
            response_header += Splitting_symbol; // 空行

            string response;
            response += response_line;
            response += response_header;
            response += response_text;

            //  std::cout << "---------------------------------" << std::endl;
            // std::cout << response << std::endl;
            //  std::cout << "---------------------------------" << std::endl;
            // std::string body = "<html><body><h1>Hello World</h1></body></html>";
            // std::string rsp = "HTTP/1.1 200 OK\r\n";
            // rsp += "Content-Length: " + std::to_string(body.size()) + "\r\n";
            // rsp += "Content-Type: text/html\r\n";
            // rsp += "\r\n";
            // rsp += body;

            send(fd, response.c_str(), response.size(), 0);
        }
    }

    static void *ThreadRun(void *argv)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(argv);
        Httphandler(td->sockfd);
        close(td->sockfd);
        delete td;
        return nullptr;
    }

private:
    St listenSock_;
    uint16_t port_;
};